package com.chorye.basictransactional.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chorye.basictransactional.dao.StudentMapper;
import com.chorye.basictransactional.entity.Student;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Chorye
 * @since 2019-06-14
 */
@Service
public class StudentService extends ServiceImpl<StudentMapper, Student> {
    /**
     * 日志记录
     */
    private final Logger log = LoggerFactory.getLogger(StudentService.class);
    /**
     * 事务管理器
     */
    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    /**
     * 事务定义信息
     */
    @Autowired
    DefaultTransactionDefinition transactionDefinition;
    /**
     * 学生数据层
     */
    @Autowired
    private StudentMapper studentMapper;

    /**
     * PROPAGATION_REQUIRED（1）：如果存在一个事务，则支持当前事务。如果没有事务则开启一个新的事务。
     * 结果：
     * ★★★ 外层事务：手动开启事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：false
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ★★★ 外层事务：手动提交事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：true
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * 说明：
     * 调用methodA方法时，因为当前上下文不存在事务，所以会开启一个新的事务。当执行到methodB时，methodB发现当前上下文有事务，因此就加入到当前事务中来。
     */
    public void propagationOneMethodA() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationOneMethodA");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("★★★ 外层事务：手动开启事务成功");
        log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            // 调用方法B
            propagationOneMethodB();
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("★★★ 外层事务：手动提交事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("★★★ 外层事务：手动回滚事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    public void propagationOneMethodB() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationOneMethodB");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_REQUIRED（1）：单独调用methodB方法时，因为当前上下文不存在事务，所以会开启一个新的事务。
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：true
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：true
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     */
    public void propagationOneMethodB2() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationOneMethodB2");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {

            Student student = new Student();
            student.setName("会被回滚");
            studentMapper.insert(student);

            // 抛出异常
            int i = 1 / 0;

            student = new Student();
            student.setName("插入不了");
            studentMapper.insert(student);

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }


    /**
     * PROPAGATION_SUPPORTS（1）：如果存在一个事务，支持当前事务。如果没有事务，则非事务的执行。但是对于事务同步的事务管理器，PROPAGATION_SUPPORTS与不使用事务有少许不同。
     * 结果：
     * ★★★ 外层事务：手动开启事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：false
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ★★★ 外层事务：手动提交事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：true
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * 说明：
     * 单纯的调用methodB时，methodB方法是非事务的执行的。当调用methdA时,methodB则加入了methodA的事务中,事务地执行
     */
    public void propagationTwoMethodA() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationTwoMethodA");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("★★★ 外层事务：手动开启事务成功");
        log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            // 调用方法B
            propagationTwoMethodB();
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("★★★ 外层事务：手动提交事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("★★★ 外层事务：手动回滚事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    public void propagationTwoMethodB() {
        // 设置传播行为为 PROPAGATION_SUPPORTS
        transactionDefinition.setPropagationBehavior(1);
        transactionDefinition.setName("事务propagationTwoMethodB");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_SUPPORTS（2）：单纯的调用methodB时，methodB方法是非事务的执行的。
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     */
    public void propagationTwoMethodB2() {
        // 设置传播行为为 PROPAGATION_SUPPORTS
        transactionDefinition.setPropagationBehavior(1);
        transactionDefinition.setName("事务propagationTwoMethodB2");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            Student student = new Student();
            student.setName("可以插入");
            studentMapper.insert(student);

            // 抛出异常
            int i = 1 / 0;

            student = new Student();
            student.setName("插入不了");
            studentMapper.insert(student);
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_MANDATORY（1）：如果已经存在一个事务，支持当前事务。如果没有一个活动的事务，则抛出异常。
     * 结果：
     * ★★★ 外层事务：手动开启事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：false
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ★★★ 外层事务：手动提交事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：true
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * 分析：
     * 当调用methodA时，methodB则加入到methodA的事务中，事务地执行。
     */
    public void propagationThreeMethodA() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationThreeMethodA");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("★★★ 外层事务：手动开启事务成功");
        log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            Student student = new Student();
            student.setName("不会被插入");
            studentMapper.insert(student);

            // 调用方法B
            propagationThreeMethodB();
            if (!transactionStatus.isRollbackOnly()) {
                // 手动提交事务
                dataSourceTransactionManager.commit(transactionStatus);
                log.info("★★★ 外层事务：手动提交事务成功");
                log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
                log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
                log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
            }

        } catch (Exception e) {
            // 手动回滚事务
            if (!transactionStatus.isRollbackOnly()) {
                dataSourceTransactionManager.rollback(transactionStatus);
                log.info("★★★ 外层事务：手动回滚事务成功");
                log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
                log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
                log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
            }
        }
    }

    public void propagationThreeMethodB() {
        // 设置传播行为为 PROPAGATION_MANDATORY
        transactionDefinition.setPropagationBehavior(2);
        transactionDefinition.setName("事务propagationThreeMethodB");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {

            Student student = new Student();
            student.setName("会被回滚");
            studentMapper.insert(student);

            int i = 1 / 0;

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            transactionStatus.setRollbackOnly();
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_MANDATORY（2）：当单独调用methodB时，因为当前没有一个活动的事务，则会抛出异常
     * org.springframework.transaction.IllegalTransactionStateException:
     * No existing transaction found for transaction marked with propagation 'mandatory'
     */
    public void propagationThreeMethodB2() {
        // 设置传播行为为 PROPAGATION_MANDATORY
        transactionDefinition.setPropagationBehavior(2);
        transactionDefinition.setName("事务propagationThreeMethodB2");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {

            Student student = new Student();
            student.setName("无法入库");
            studentMapper.insert(student);

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_REQUIRES_NEW（1）：使用PROPAGATION_REQUIRES_NEW，它会开启一个新的事务。如果一个事务已经存在，则先将这个存在的事务挂起。
     * 结果：
     * ★★★ 外层事务：手动开启事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：false
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：true
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：true
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ★★★ 外层事务：手动回滚事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：true
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * 分析：
     * 内层事务是否成功并不依赖于外层事务，如果methodA方法在调用methodB方法后的方法失败了，而methodB方法所做的结果依然被提交。而除了 methodB之外的其它代码导致的结果却被回滚了
     */
    public void propagationFourMethodA() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationFourMethodA");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("★★★ 外层事务：手动开启事务成功");
        log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            // 调用方法B
            propagationFourMethodB();
            // 出现异常
            int i = 1 / 0;
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("★★★ 外层事务：手动提交事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("★★★ 外层事务：手动回滚事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    public void propagationFourMethodB() {
        // 设置传播行为为 PROPAGATION_REQUIRES_NEW
        transactionDefinition.setPropagationBehavior(3);
        transactionDefinition.setName("事务propagationFourMethodB");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {

            Student student = new Student();
            student.setName("可以插入");
            studentMapper.insert(student);

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_REQUIRES_NEW（2）：单独执行方法B，会开启事务执行
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：true
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：true
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     */
    public void propagationFourMethodB2() {
        // 设置传播行为为 PROPAGATION_REQUIRES_NEW
        transactionDefinition.setPropagationBehavior(3);
        transactionDefinition.setName("事务propagationFourMethodB2");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {

            Student student = new Student();
            student.setName("会被回滚");
            studentMapper.insert(student);

            // 抛出异常
            int i = 1 / 0;

            student = new Student();
            student.setName("插入不了");
            studentMapper.insert(student);

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_NOT_SUPPORTED（1）：总是非事务地执行，并挂起任何存在的事务。使用PROPAGATION_NOT_SUPPORTED
     * 结果：
     * ★★★ 外层事务：手动开启事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：false
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动回滚事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ★★★ 外层事务：手动提交事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：true
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * 分析：
     * 执行到方法B时，会挂起事务A，然后以非事务的方式执行方法B，如果方法B抛出异常，方法A不会回滚，不影响A的结果
     * <p>
     */
    public void propagationFiveMethodA() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationFiveMethodA");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("★★★ 外层事务：手动开启事务成功");
        log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            Student student = new Student();
            student.setName("可以插入");
            studentMapper.insert(student);

            // 调用方法B
            propagationFiveMethodB();
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("★★★ 外层事务：手动提交事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("★★★ 外层事务：手动回滚事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    public void propagationFiveMethodB() {
        // 设置传播行为为 PROPAGATION_NOT_SUPPORTED
        transactionDefinition.setPropagationBehavior(4);
        transactionDefinition.setName("事务propagationFiveMethodB");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            int i = 1 / 0;
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_NOT_SUPPORTED（2）：单独运行方法B，不在事务下运行
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     */
    public void propagationFiveMethodB2() {
        // 设置传播行为为 PROPAGATION_NOT_SUPPORTED
        transactionDefinition.setPropagationBehavior(4);
        transactionDefinition.setName("事务propagationFiveMethodB2");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            Student student = new Student();
            student.setName("可以插入");
            studentMapper.insert(student);

            // 抛出异常
            int i = 1 / 0;

            student = new Student();
            student.setName("插入不了");
            studentMapper.insert(student);
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_NEVER（1）：总是非事务地执行，如果存在一个活动事务，则抛出异常。
     * 结果：
     * ★★★ 外层事务：手动开启事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：false
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ★★★ 外层事务：手动回滚事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：true
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * 分析：
     * 内层事务因为外层事务的存在，抛出了异常，外层事务进行回滚
     */
    public void propagationSixMethodA() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationSixMethodA");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("★★★ 外层事务：手动开启事务成功");
        log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            Student student = new Student();
            student.setName("被回滚了");
            studentMapper.insert(student);

            // 调用方法B
            propagationSixMethodB();
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("★★★ 外层事务：手动提交事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("★★★ 外层事务：手动回滚事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_NEVER（2）：单独执行方法B，以非事务的方式运行
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     */
    public void propagationSixMethodB() {
        // 设置传播行为为 PROPAGATION_NEVER
        transactionDefinition.setPropagationBehavior(5);
        transactionDefinition.setName("事务propagationSixMethodB");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {

            Student student = new Student();
            student.setName("可以插入");
            studentMapper.insert(student);

            // 抛出异常
            int i = 1 / 0;

            student = new Student();
            student.setName("插入不了");
            studentMapper.insert(student);
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_NESTED（1）：如果一个活动的事务存在，则运行在一个嵌套的事务中。 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
     * 这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。
     * 需要JDBC 驱动的java.sql.Savepoint类。使用PROPAGATION_NESTED，还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true(属性值默认为false)。
     * <p>
     * 结果：
     * ★★★ 外层事务：手动开启事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：false
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ★★★ 外层事务：手动回滚事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：true
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * 分析：
     * 外层事务失败时，会回滚内层事务所做的动作
     */
    public void propagationSevenMethodA() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationSevenMethodA");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("★★★ 外层事务：手动开启事务成功");
        log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            // 调用方法B
            propagationSevenMethodB();
            // 此处抛出错误
            int i = 1 / 0;
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("★★★ 外层事务：手动提交事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("★★★ 外层事务：手动回滚事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    public void propagationSevenMethodB() {
        // 设置传播行为为 PROPAGATION_NESTED
        transactionDefinition.setPropagationBehavior(6);
        transactionDefinition.setName("事务propagationSevenMethodB");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            Student student = new Student();
            student.setName("被回滚");
            studentMapper.insert(student);
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_NESTED（1）：如果一个活动的事务存在，则运行在一个嵌套的事务中。 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
     * 这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。
     * 需要JDBC 驱动的java.sql.Savepoint类。使用PROPAGATION_NESTED，还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true(属性值默认为false)。
     * <p>
     * 结果：
     * ★★★ 外层事务：手动开启事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：false
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动回滚事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：false
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ★★★ 外层事务：手动提交事务成功
     * ★★★ 外层事务：事务是否已提交或回滚：true
     * ★★★ 外层事务：当前事务是否是新的：true
     * ★★★ 外层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * 分析：
     * 内层事务失败时，不会回滚外层事务所做的动作
     */
    public void propagationSevenMethodA2() {
        // 设置传播行为为 PROPAGATION_REQUIRED
        transactionDefinition.setPropagationBehavior(0);
        transactionDefinition.setName("事务propagationSevenMethodA2");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("★★★ 外层事务：手动开启事务成功");
        log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            // 调用方法B
            propagationSevenMethodB2();

            Student student = new Student();
            student.setName("不会被回滚");
            studentMapper.insert(student);
            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("★★★ 外层事务：手动提交事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("★★★ 外层事务：手动回滚事务成功");
            log.info("★★★ 外层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("★★★ 外层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("★★★ 外层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    public void propagationSevenMethodB2() {
        // 设置传播行为为 PROPAGATION_NESTED
        transactionDefinition.setPropagationBehavior(6);
        transactionDefinition.setName("事务propagationSevenMethodB2");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            // 抛出异常
            int i = 1 / 0;

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }

    /**
     * PROPAGATION_NESTED（3）：单独执行B事务
     * 结果：
     * ☆☆☆ 内层事务：手动开启事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：false
     * ☆☆☆ 内层事务：当前事务是否是新的：true
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * ☆☆☆ 内层事务：手动提交事务成功
     * ☆☆☆ 内层事务：事务是否已提交或回滚：true
     * ☆☆☆ 内层事务：当前事务是否是新的：true
     * ☆☆☆ 内层事务：返回事务是否已标记为仅回滚：false
     * <p>
     * 分析：
     * 按REQUIRED属性执行
     */
    public void propagationSevenMethodB3() {
        // 设置传播行为为 PROPAGATION_NESTED
        transactionDefinition.setPropagationBehavior(6);
        transactionDefinition.setName("事务propagationSevenMethodB3");
        // 手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        log.info("☆☆☆ 内层事务：手动开启事务成功");
        log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
        log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
        log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        try {
            Student student = new Student();
            student.setName("被回滚");
            studentMapper.insert(student);

            // 抛出异常
            int i = 1 / 0;

            student = new Student();
            student.setName("插入不了");
            studentMapper.insert(student);

            // 手动提交事务
            dataSourceTransactionManager.commit(transactionStatus);
            log.info("☆☆☆ 内层事务：手动提交事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());

        } catch (Exception e) {
            // 手动回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            log.info("☆☆☆ 内层事务：手动回滚事务成功");
            log.info("☆☆☆ 内层事务：事务是否已提交或回滚：{}", transactionStatus.isCompleted());
            log.info("☆☆☆ 内层事务：当前事务是否是新的：{}", transactionStatus.isNewTransaction());
            log.info("☆☆☆ 内层事务：返回事务是否已标记为仅回滚：{}", transactionStatus.isRollbackOnly());
        }
    }
}
